package suncertify.db;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * This class is a wrapper class to access database file.
 * <p>
 * This class is owner to all access to database, there is no logic in this 
 * class, all request will be attend. This class is faithful to DB interface
 * and treat all data as String[], as implement give it, to be keep code reuse.
 * <p>
 * So, we have to layers of abstraction {@link Data} and {@link DataProvider}, 
 * a Adapter class need to be implemented to convert String[] to data of system. 
 * <p>
 * As this class should be used only by Data class it was set as package access.
 * 
 * @author Edson Pereira
 * @version 1.0
 */
class DataProvider {

    /* Private members */
    /* Keep dabase file information*/
    private int magic_cookie = 0;
    private int record_lenght = 0;
    private int record_fields = 0;
    private int field_name_lenght = 0;
    private int field_lenght = 0;
    
    private Map<Integer, String[]> dataCache = new HashMap<Integer, String[]>();
    private RandomAccessFile dataProvider;
    private String path;
    
    
    /* 
     * Database Information
     * sizes fields, provided by specification.
     * Nuber of bytes in each database field
     */
    private static final int MAGIC_COOKIE_BYTES = 4;
    private static final int RECORD_LENGHT_BYTES = 4;
    private static final int RECORD_FIELDS_BYTES = 2;
    private static final int FIELD_NAME_LENGHT_BYTES = 2;
    private static final int FIELD_LENGHT_BYTES = 2;
    
    private static final int HOTEL_NAME_BYTES = 64;
    private static final int CITY_NAME_BYTES = 64;
    private static final int ROOM_SIZE_BYTES = 4;
    private static final int IS_SMOKING_BYTE = 1;
    private static final int PRICE_BYTES = 8;
    private static final int DATE_BYTES = 10;
    private static final int CUSTOMER_BYTES = 8;
    
    /* Character encoding */
    private static final String ENCODING_8_BITS_US_ASCII = "ASCII";
    
    /**  
     * Create an instance of DataProvider, this class offers all database
     * functionality. Also, populate cache with record values
     * 
     *  @param path Path of database file
     *  @throws FileNotFoundException Indicates the database was not found
     *  @throws IOException Indicates fail or interrupted I/O operation
     */
    DataProvider(String path) throws FileNotFoundException, IOException{
        this.path = path;
        dataProvider = new RandomAccessFile(path, "rw");
        readData();
    }
    
    /** 
     * Reads a record from file. 
     *
     * @param recNo Record number to be read from file
     * @return Returns an array where each element is a record value
     * @throws RecordNotFoundException Indicates record number does not 
     *         exist or was marked as deleted.
     */
    public String[] read(int recNo) throws RecordNotFoundException {
        String[] result = null;
        if (dataCache.containsKey(recNo)){
            result = dataCache.get(recNo);
        }else{
            throw new RecordNotFoundException();
        }
        return result;
    }

    /**
     * Modifies fields of a record. 
     * <p>The new value for field n appears in data[n].
     *
     * @param recNo Record number to be read from file
     * @param data Array of {@code String} where each element is a record
     *        value
     * @throws RecordNotFoundException Indicates record number does not exist 
     *         or marked as deleted.
     */
    public void update(int recNo, String[] data)
            throws RecordNotFoundException {
        if (dataCache.containsKey(recNo)){
            dataCache.put(recNo, data);
        }else{
            throw new RecordNotFoundException();
        }
    }

    /**
     * Deletes a record making the record number and associated disk storage
     * available for reuse.
     *
     * @param recNo Record number to be read from file
     * @throws RecordNotFoundException Indicates record number does not exist 
     *         or marked as deleted.
     */
    public void delete(int recNo)
            throws RecordNotFoundException {
        if (dataCache.containsKey(recNo)){
            dataCache.remove(recNo);
        }else{
            throw new RecordNotFoundException();
        }
    }
    
    /**
     * Search for records that match the specified criteria. 
     *
     * <p>Field {@code n} in the database file is described by criteria
     *    [{@code n}]. A null 
     * value in criteria[{@code n}] matches any field value. 
     * <p>A non-null value in criteria[{@code n}] matches any field value that
     * begins with criteria[{@code n}].
     * For example, "Fred" matches "Fred" or "Freddy".) 
     *
     * @param criteria Array of {@code String} where each element is a record
     *        value
     * @return An array of record numbers that match the specified criteria. 
     */
    public int[] find(String[] criteria) {
        List<Integer> resultList = new ArrayList<Integer>();
        for (String[] entry : dataCache.values()){
            for (int i=0; i<entry.length; i++){
                
            }
        }
        int[] result = new int[resultList.size()];
        
        return result;
    }
    
    /**
     * Creates a new record in the database
     * @param data Array of {@code String} where each element is a record
     *        value
     * @return The {@code int} record number of the new record
     * @throws DuplicateKeyException Record already created
     */
    public int create(String[] data) throws DuplicateKeyException {
        return 0;
    }
    
    private void readData() throws IOException{
        byte[] magic_cookie_byte = new byte[MAGIC_COOKIE_BYTES];
        byte[] record_lenght_byte = new byte[RECORD_LENGHT_BYTES];
        byte[] record_fields_byte = new byte[RECORD_FIELDS_BYTES];

        synchronized(dataProvider){
            dataProvider.read(magic_cookie_byte, 0, MAGIC_COOKIE_BYTES);
            dataProvider.read(record_lenght_byte, 0, RECORD_LENGHT_BYTES);
            dataProvider.read(record_fields_byte, 0, RECORD_FIELDS_BYTES);
            
            
            
        }
        /* keep database information */
        magic_cookie = convertByteArrayToInt(magic_cookie_byte);
        record_lenght = convertByteArrayToInt(record_lenght_byte);
        record_fields = convertByteArrayToInt(record_fields_byte);
    }
    
    private int convertByteArrayToInt(byte[] byteArray){
        int length = byteArray.length;
        int result = 0;

        for(int i = 0; i<length; i++){
            final int shift = (length - 1 - i) * 8;  
            result += (byteArray[i] & 0x000000FF) << shift;
        }

        return result;
    }
}
